package com.github.xzb617.client.flow.core.system;

import com.github.xzb617.client.flow.cache.CounterCache;
import com.github.xzb617.client.flow.core.FlowContext;
import com.github.xzb617.client.flow.core.FlowException;
import com.github.xzb617.client.flow.core.FlowLimiter;
import com.github.xzb617.client.flow.core.FlowProperties;
import com.github.xzb617.client.flow.vm.MxInfo;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.Set;

/**
 * 系统保护性限流
 * <p>
 *     1.根据三种规则，QPS、CPU负载占比、Memory使用占比
 *     2.一旦达到某个规则的阈值，就自动限流，已到达保护应用服务的目的
 * </p>
 * @author xzb
 */
public class SystemProtectedFlowLimiter implements FlowLimiter {

    @Override
    public boolean enabled(FlowProperties flowConfig) {
        FlowProperties.SystemProtectedProperties systemProtected = flowConfig.getSystem();
        return Boolean.TRUE.equals(systemProtected.getEnabled());
    }

    @Override
    public void executeInternal(FlowContext flowContext, FlowProperties flowConfig, CounterCache counterCache, FlowLimiter limiterChain) throws FlowException {
        // 获取规则
        Map<String, Integer> rules = flowConfig.getSystem().getRules();

        if (rules!=null && !rules.isEmpty()) {
            // 获取实时CPU和内存占用率
            MxInfo mxInfo = MxInfo.getMxInfo();
            double memoryUsedRatio = mxInfo.memoryUsedRatio();
            double cpuUsedRatio = mxInfo.cpuUsedRatio();

            // 根据不同规则判断
            boolean result = false;
            Set<Map.Entry<String, Integer>> entries = rules.entrySet();
            for (Map.Entry<String, Integer> entry : entries) {
                String ruleType = entry.getKey();
                int threshold   = entry.getValue();
                switch (ruleType) {
                    case SpRuleType.QPS:
                        result = flowContext.getQps() > threshold;
                        break;
                    case SpRuleType.CPU:
                        result = gtCpuUsedThreshold(threshold, cpuUsedRatio);
                        break;
                    case SpRuleType.MEMORY:
                        result = gtMemoryUsedThreshold(threshold, memoryUsedRatio);
                        break;
                    default:
                        throw new IllegalArgumentException("Illegal system protection rules.");
                }
                if (result) break;
            }

            // 触发限流
            if (result) {
                throw new FlowException("Too Many Request");
            }
        }
    }

    /**
     * 超过CPU使用负载的阈值
     * @param threshold 阈值
     * @param currentCpuUsedRation CPU占用率
     * @return
     */
    private static boolean gtCpuUsedThreshold(int threshold, double currentCpuUsedRation) {
        return currentCpuUsedRation > threshold;
    }

    /**
     * 超过内存占用率的阈值
     * @param threshold 阈值
     * @param currentMemoryUsedRation 内存占用率
     * @return
     */
    private static boolean gtMemoryUsedThreshold(int threshold, double currentMemoryUsedRation) {
        return currentMemoryUsedRation > threshold;
    }
}
